/**
 * Copyright (c) 2010-2011 julewa.com.  
 * All rights reserved. 
 * 
 * @author Huang Benhua
 * @date 2011-8-2
 * 
**/
package com.julewa.utils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

@SuppressWarnings("rawtypes")
public class ClassUtils {
	/***
	 * 非常重要的工具类 
	 */
	/**
	 * 获取某一个成员的真正类型
	 * @param actualType
	 * @param field
	 * @return
	 */
	public static Type[] getActualFieldTypeDef(
			Type actualType,
			Field field
	){
		return getActualTypeDef(field.getDeclaringClass(), field.getGenericType(), actualType);
	}
	
	/**
	 * 获取某一个成员的真正类型
	 * @param declareClass
	 * @param fieldName
	 * @param actualType
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 */
	public static Type[] getActualFieldTypeDef(
			Class<?> declareClass, 
			String fieldName, 
			Type actualType
	) throws SecurityException, NoSuchFieldException{
		Field varField = declareClass.getDeclaredField(fieldName);
		return getActualTypeDef(declareClass, varField.getGenericType(), actualType);
	}
	
	/**
	 * 获取某一个成员的真正类型
	 * eg:
	 * class A<E>{
	 *   public E var;
	 * }
	 * 
	 * class B<F extends Number> extends A<F>{
	 *   
	 * }
	 * 
	 * class C extends B<Long>{
	 *   
	 * }
	 * 
	 * 想获取var在C中的实际类型,需要如下调用
	 * Field varField = A.class.getDeclaredField("var");
	 * Type realTypeOfVar = getActualTypeDef(A.class, varField.getGenericType(), C.class)[0];
	 * 
	 * @param declareClass
	 * @param memberType
	 * @param actualType
	 * @return memberType在actualType中的实际表示形态
	 * 返回值第一个为rawType,后面是argumentsType
	 * 如Map<K, V>就会返回[Map.class, Long.class, User.class]
	 */
	public static Type[] getActualTypeDef(
			Class<?> declareClass, 
			Type memberType, 
			Type actualType
	){
		return getActualTypeDef(getActualArgumentTypesMap(actualType, declareClass), memberType);
	}
	
	/**
	 * 根据环境变量的实际值换算memberType的actualTypes
	 * @param typeValues
	 * @param memberType
	 * @return
	 */
	public static Type[] getActualTypeDef(
			Map<TypeVariable<?>, Type> typeValues, 
			Type memberType
	){
		if(memberType == null)return null;
		Type ats[] = {memberType};
		if(memberType instanceof Class)return ats;
		if(memberType instanceof ParameterizedType){
			ParameterizedType pt = (ParameterizedType)memberType;
			Type ts[] = pt.getActualTypeArguments();
			ats = new Type[ts.length + 1];
			for(int i = 0; i < ts.length; i ++){
				ats[i + 1] = ts[i];
			}
			ats[0] = memberType;
		}
		if(typeValues != null && !typeValues.isEmpty()){
			for(int i = 0; i < ats.length; i ++){
				Type t = ats[i];
				if(t instanceof TypeVariable){
					t = typeValues.get(t);
					if(t != null)ats[i] = t;
				}
			}
		}
		return ats;
	}
	
	/**
	 * 获取actualType对targetClass参数的实际对应值表
	 * @param actualType
	 * @param targetClass
	 * @return
	 */
	public static Map<TypeVariable<?>, Type> getActualArgumentTypesMap(
			Type actualType, 
			Class<?> targetClass
	){
		return createActualArgumentTypesMap(targetClass, getActualArgumentTypes(actualType, targetClass));
	}
	
	/**
	 * 获取actualType对targetClass参数的实际对应值数组
	 * @param actualType
	 * @param targetClass
	 * @return
	 */
	public static Type[] getActualArgumentTypes(
			Type actualType, 
			Class<?> targetClass
	){
		TypeVariable totv[] = targetClass.getTypeParameters();
		if(totv == null || totv.length < 1)return null;//没有任何意义
		Class<?> from = null;
		Type types[] = null;
		if(actualType instanceof ParameterizedType){
			from = (Class)((ParameterizedType)actualType).getRawType();
			types = ((ParameterizedType)actualType).getActualTypeArguments();
		}else{
			from = (Class)actualType;
		}
		if(!targetClass.isAssignableFrom(from))return null;
		return findActualArgumentTypes(types, from, targetClass);
	}
	
	/**
	 * 生成数组与参数映射表
	 * @param targetClass
	 * @param actualArgTypes
	 * @return
	 */
	private static Map<TypeVariable<?>, Type> createActualArgumentTypesMap(
			Class<?> targetClass, 
			Type[] actualArgTypes
	){
		TypeVariable<?> tvs[] = targetClass.getTypeParameters();
		Map<TypeVariable<?>, Type> tvmap = new HashMap<TypeVariable<?>, Type>();
		if(tvs != null && actualArgTypes != null)for(int i = 0; i < tvs.length; i ++){
			tvmap.put(tvs[i], actualArgTypes[i]);
		}
		if(tvmap.isEmpty())tvmap = null;
		return tvmap;
	}
	
	/**
	 * 
	 * @param childActualArgTypes
	 * @param actualClass
	 * @param targetClass
	 * @return
	 */
	private static Type[] findActualArgumentTypes(
			Type[] childActualArgTypes, 
			Class<?> actualClass, 
			Class<?> targetClass
	){
		if(actualClass == null)return null;
		if(actualClass.equals(targetClass))return childActualArgTypes;
		Map<TypeVariable<?>, Type> tvmap = createActualArgumentTypesMap(actualClass, childActualArgTypes);
		if(!actualClass.isInterface()){
			Type ats[] = null;
			ats = getActualArgumentTypeValues(tvmap, actualClass.getGenericSuperclass(), targetClass);
			if(ats != null)return ats;
		}
		Type ts[] = actualClass.getGenericInterfaces();
		if(ts != null)for(Type t:ts){
			Type ats[] = getActualArgumentTypeValues(tvmap, t, targetClass);
			if(ats == null)continue;
			return ats;
		}
		return null;
	}
	
	/**
	 * 
	 * @param typeValuesMap
	 * @param superActualType
	 * @param targetClass
	 * @return
	 */
	private static Type[] getActualArgumentTypeValues(
			Map<TypeVariable<?>, Type> typeValuesMap, 
			Type superActualType, 
			Class<?> targetClass
	){
		if(superActualType == null)return null;
		if(superActualType instanceof ParameterizedType){
			ParameterizedType pt = (ParameterizedType)superActualType;
			if(!targetClass.isAssignableFrom((Class)pt.getRawType()))return null;
			Type ts[] = pt.getActualTypeArguments();
			if(typeValuesMap != null && ts != null){
				for(int i = 0; i < ts.length; i ++){
					Type t = ts[i];
					if(t instanceof Class) continue;//表示已经找到
					ts[i] = typeValuesMap.get((TypeVariable)t);
				}
			}
			if(targetClass.equals(pt.getRawType()))return ts;
			return findActualArgumentTypes(ts, (Class)pt.getRawType(), targetClass);
		}else{
			if(!targetClass.isAssignableFrom((Class)superActualType))return null;
			return findActualArgumentTypes(null, (Class)superActualType, targetClass);
		}
	}
}
